package com.ungao.framework.context;

import com.ungao.framework.ApplicationContext;
import com.ungao.framework.annotation.Autowired;
import com.ungao.framework.annotation.Component;
import com.ungao.framework.convert.CaseConverter;
import com.ungao.framework.define.BeanDefinition;
import com.ungao.framework.exception.MultipartBeanException;
import com.ungao.framework.exception.RegisterBeanException;
import com.ungao.framework.aware.InitializeAware;
import com.ungao.framework.proxy.ProxyUtils;
import com.ungao.framework.scanner.AnnotationScanner;
import com.ungao.framework.scanner.PackageScanner;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

abstract class AbstractApplicationContext implements ApplicationContext {

    /**
     * Bean对象池
     */
    protected final Map<String, BeanDefinition> beanFactory = new ConcurrentHashMap<>();

    /**
     * 接口扩展
     */
    protected final Map<Class, InitializeAware> factories = new ConcurrentHashMap<>();

    /**
     * 包扫描类
     */
    protected final PackageScanner scanner = new PackageScanner();

    /**
     * 注解扫描类
     */
    protected final AnnotationScanner annotations = new AnnotationScanner();

    /**
     * 初始化容器
     *
     * @param classNames
     */
    protected void startup(List<String> classNames) {

        //容器初始化执行方法
        this.initApplicationContext(annotations);

        //注册BeanDefinition至beanFactory
        this.initRegister(classNames);

        //初始化扩展点
        //为什么在这个初始化扩展点
        //因为还没有执行依赖注入、动态代理，方便扩展点注册自己管理的对象BeanDefinition
        this.initializeFactory(this);

        //刷新beanFactory，执行依赖注入、动态代理
        this.refresh();
    }

    /**
     * 注册beanFactory
     *
     * @param classNames
     * @throws RegisterBeanException
     */
    private void initRegister(List<String> classNames) throws RegisterBeanException {
        try {
            for (String className : classNames) {

                //类加载
                Class<?> clazz = Class.forName(className);

                //扩展点扫描
                if (InitializeAware.class.isAssignableFrom(clazz)) {
                    factories.putIfAbsent(clazz, (InitializeAware) ProxyUtils.cglibProxy(clazz.newInstance()));
                }

                //外部扩展注解
                for (Class<? extends Annotation> annotation : annotations.getExtends()) {
                    if (clazz.isAnnotationPresent(annotation)) {

                        //定义BeanDefinition
                        Object instance = clazz.newInstance();
                        BeanDefinition beanDefinition = new BeanDefinition(instance, instance);
                        beanFactory.putIfAbsent(CaseConverter.toLowerCase(clazz.getSimpleName()), beanDefinition);
                    }
                }

                //容器自带注解
                for (Class<? extends Annotation> annotation : annotations.getContainers()) {
                    if (clazz.isAnnotationPresent(annotation)) {

                        //类名首字母小写
                        String beanName = CaseConverter.toLowerCase(clazz.getSimpleName());

                        //自定义BeanName
                        if (Component.class.isAssignableFrom(annotation)) {
                            Component component = (Component) clazz.getAnnotation(annotation);
                            if (!"".equals(component.value().trim())) {
                                beanName = component.value().trim();
                            }
                        }

                        //存储BeanDefinition
                        Object instance = clazz.newInstance();
                        beanFactory.putIfAbsent(beanName, new BeanDefinition(instance, instance));

                        //接口必须创建实现类实例
                        for (Class<?> face : clazz.getInterfaces()) {
                            BeanDefinition interfaces;
                            final String faceName = face.getName();
                            if (beanFactory.containsKey(faceName)) {
                                interfaces = beanFactory.get(faceName);
                            } else {
                                interfaces = new BeanDefinition();
                            }
                            interfaces.put(beanName, instance);
                            beanFactory.putIfAbsent(faceName, interfaces);
                        }
                        break;
                    }
                }
            }
        } catch (IllegalAccessException | InstantiationException | ClassNotFoundException | RegisterBeanException e) {
            throw new RegisterBeanException(e);
        }
    }

    /**
     * 刷新beanFactory，执行依赖注入、动态代理
     */
    private void refresh() {
        this.injectionAttribute();
        this.dynamicProxyObject();
    }

    /**
     * 依赖注入
     */
    private void injectionAttribute() {
        try {
            for (Map.Entry<String, BeanDefinition> entry : beanFactory.entrySet()) {

                //获取bean定义信息
                BeanDefinition beanDefinition = entry.getValue();

                //接口不做任何处理
                if (beanDefinition.isInterface()) {
                    continue;
                }

                //拿到所有的字段，包括private/public/protected/default
                Object original = beanDefinition.getOriginal();
                for (Field field : original.getClass().getDeclaredFields()) {
                    if (!field.isAnnotationPresent(Autowired.class)) {
                        continue;
                    }

                    //获取当前数据
                    Autowired autowired = field.getAnnotation(Autowired.class);
                    String beanName = autowired.value().trim();
                    if ("".equals(beanName)) {

                        //如果是接口，直接使用接口名称,否则使用类名首字母小写
                        Class<?> type = field.getType();
                        if (type.isInterface()) {
                            beanName = type.getName();
                        } else {
                            beanName = CaseConverter.toLowerCase(type.getSimpleName());
                        }
                    }

                    //强制访问、属性赋值
                    field.setAccessible(true);
                    field.set(original, this.getBean(beanName));
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 动态代理
     */
    private void dynamicProxyObject() {
        for (Map.Entry<String, BeanDefinition> entry : beanFactory.entrySet()) {

            //获取bean定义信息
            BeanDefinition beanDefinition = entry.getValue();

            //接口不做任何处理
            if (beanDefinition.isInterface()) {
                continue;
            }

            //拿到所有的字段，包括private/public/protected/default
            Object original = beanDefinition.getOriginal();
            beanDefinition.setProxyBean(ProxyUtils.cglibProxy(original));

            //接口，必须创建实现类的实例
            for (Class<?> face : original.getClass().getInterfaces()) {
                BeanDefinition interfaces = beanFactory.get(face.getName());
                interfaces.put(entry.getKey(), beanDefinition.getProxyBean());
            }
        }
    }

    /**
     * 初始化扩展点
     *
     * @param applicationContext
     */
    private void initializeFactory(ApplicationContext applicationContext) {
        for (Map.Entry<Class, InitializeAware> factory : factories.entrySet()) {
            InitializeAware initializeAware = factory.getValue();
            initializeAware.setApplicationContext(applicationContext);
            initializeAware.initialize();
        }
    }

    @Override
    public <T> T getBean(Class<T> clazz) throws MultipartBeanException {
        if (clazz.isInterface()) {
            return this.getBean(clazz.getName());
        } else {
            String beanName = CaseConverter.toLowerCase(clazz.getSimpleName());
            return (T) beanFactory.get(beanName).getProxyBean();
        }
    }

    @Override
    public <T> T getBean(String beanName) throws MultipartBeanException {
        Object instance = null;

        BeanDefinition beanDefinition = beanFactory.get(beanName);
        Map<String, Object> singletonBeans = beanDefinition.getSingletonBeans();
        if (singletonBeans.size() > 1) {
            throw new MultipartBeanException("There are multiple implementation classes!");
        }

        for (Map.Entry<String, Object> entry : singletonBeans.entrySet()) {
            instance = entry.getValue();
            if (instance != null) {
                break;
            }
        }

        if (instance == null) {
            instance = beanDefinition.getProxyBean();
        }

        return (T) instance;
    }

    @Override
    public void registerDefinition(String beanName, BeanDefinition beanDefinition) throws RegisterBeanException {
        if (beanFactory.containsKey(beanName)) {
            BeanDefinition proxyDefinition = beanFactory.get(beanName);
            proxyDefinition.setOriginal(proxyDefinition.getOriginal());
            proxyDefinition.setProxyBean(proxyDefinition.getProxyBean());
            proxyDefinition.put(beanName, proxyDefinition.getProxyBean());
        } else {
            beanFactory.putIfAbsent(beanName, beanDefinition);
        }
    }

    @Override
    public String getBeanName(Class clazz) {
        String beanName;
        if (clazz.isInterface()) {
            beanName = clazz.getName();
        } else {
            beanName = CaseConverter.toLowerCase(clazz.getName());
        }
        return beanName;
    }

    /**
     * 容器初始化执行方法
     *
     * @param annotations
     * @throws RegisterBeanException
     */
    protected abstract void initApplicationContext(AnnotationScanner annotations) throws RegisterBeanException;
}
